/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "accessibility_espeak_player.h"
#include "hilog_wrapper.h"
#include <unistd.h>

#ifndef ESPEAK_TAG
#define ESPEAK_TAG "ESPEAK_TAG| "
#endif

namespace OHOS {
namespace Accessibility {

EspeakPlayer::EspeakPlayer()
{

}

EspeakPlayer::~EspeakPlayer()
{

}

bool EspeakPlayer::Init()
{
    HILOG_DEBUG(ESPEAK_TAG "EspeakPlayer Init");
    // 初始化renderer
    AudioStandard::AudioStreamType streamType = AudioStandard::AudioStreamType::STREAM_ACCESSIBILITY;
    renderer_ = AudioStandard::AudioRenderer::Create(streamType);
    if(renderer_ == nullptr){
        HILOG_ERROR(ESPEAK_TAG "Init faied, audioRenderer is nullptr");
        return false;
    }
    AudioStandard::AudioRendererParams rendererParams;
    rendererParams.sampleFormat = AudioStandard::AudioSampleFormat::SAMPLE_S16LE;
    rendererParams.sampleRate = AudioStandard::AudioSamplingRate::SAMPLE_RATE_22050;
    rendererParams.channelCount = AudioStandard::AudioChannel::MONO;
    rendererParams.encodingType = AudioStandard::AudioEncodingType::ENCODING_PCM;
    if(renderer_->SetParams(rendererParams)!=0){
        HILOG_ERROR(ESPEAK_TAG "SetParams failed");
        return false;
    }

    supportedSampleList_=AudioStandard::AudioRenderer::GetSupportedSamplingRates();
    supportedChannelsList_=AudioStandard::AudioRenderer::GetSupportedChannels();
    if(supportedSampleList_.empty() || supportedChannelsList_.empty()){
        HILOG_ERROR(ESPEAK_TAG "supportedSampleList or supportedChannelsList is empty");
    }

    // 启动播放线程
    playThread_ = std::thread(&EspeakPlayer::PlayBackThread, this);

    HILOG_DEBUG(ESPEAK_TAG "EspeakPlayer Init success");
    return true;
}

bool EspeakPlayer::Release()
{
    HILOG_DEBUG(ESPEAK_TAG "EspeakPlayer Release");
    // 等待播放结束
    int count = 0; // 防止死循环，最多等1s
    while(IsPlaying() && count < 10){
        HILOG_DEBUG(ESPEAK_TAG "Espeakplayer is playing");
        count++;
        usleep(200000); // 200ms
    }
    //先释放播放线程资源
    exitFlag_ = true;
    Interrupt();
    if(playThread_.joinable()){
        playThread_.join();
    }
    if(renderer_){
        renderer_->Release();
        renderer_ = nullptr;
    }
    HILOG_DEBUG(ESPEAK_TAG "EspeakPlayer Release success");
    return true;
}

bool EspeakPlayer::Start()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Start failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Start()){
        HILOG_DEBUG(ESPEAK_TAG "Start failed");
        return false;
    }
    
    HILOG_DEBUG(ESPEAK_TAG "Start success");
    return true;
}

bool EspeakPlayer::Pause()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Pause failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Pause()){
        HILOG_DEBUG(ESPEAK_TAG "Pause failed");
        return false;
    }
    
    HILOG_DEBUG(ESPEAK_TAG "Pause success");
    return true;
}

bool EspeakPlayer::Stop()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Stop failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Stop()){
        HILOG_DEBUG(ESPEAK_TAG "Stop failed");
        return false;
    }
    
    HILOG_DEBUG(ESPEAK_TAG "Stop success");
    return true;
}
 
bool EspeakPlayer::Drain()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Drain failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Drain()){
        HILOG_DEBUG(ESPEAK_TAG "Drain failed");
        return false;
    }
    HILOG_DEBUG(ESPEAK_TAG "Drain success");
    return true;
}

bool EspeakPlayer::Flush()
{
    HILOG_DEBUG(ESPEAK_TAG);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "Flush failed, audioRenderer is nullptr");
        return false;
    }
    if(!renderer_->Flush()){
        HILOG_DEBUG(ESPEAK_TAG "Flush failed");
        return false;
    }
    HILOG_DEBUG(ESPEAK_TAG "Flush success");
    return true;
}

bool EspeakPlayer::SetParams(int32_t channels, int32_t sampleRate)
{
    HILOG_DEBUG(ESPEAK_TAG "channels:%{public}d, sampleRate:%{public}d", channels, sampleRate);
    if(!renderer_){
        HILOG_ERROR(ESPEAK_TAG "SetParams failed, audioRenderer is nullptr");
        return false;
    }
    AudioStandard::AudioRendererParams rendererParams;
    bool flag1=false, flag2=false; // 判断是否支持
    for(auto &c : supportedChannelsList_){
        if(c == channels){
            rendererParams.channelCount = c;
            flag1 = true;
            break;
        }
    }
    //尽可能刚好覆盖数据的采样率
    for(auto &s : supportedSampleList_){
        if(sampleRate<=s){
            rendererParams.sampleRate=s;
            flag2 = true;
            break;
        }
    }
    if(!flag1 || !flag2){
        HILOG_ERROR(ESPEAK_TAG "SetParams failed, channels:%{public}d, sampleRate:%{public}d", channels, sampleRate);
        return false;
    }
    HILOG_DEBUG(ESPEAK_TAG "find supported channels:%{public}d, sampleRate:%{public}d", rendererParams.channelCount, rendererParams.sampleRate);
    rendererParams.sampleFormat = AudioStandard::AudioSampleFormat::SAMPLE_S16LE;
    rendererParams.encodingType = AudioStandard::AudioEncodingType::ENCODING_PCM;
    if(renderer_->SetParams(rendererParams)!=0){
        HILOG_ERROR(ESPEAK_TAG "SetParams failed");
        return false;
    }
    HILOG_DEBUG(ESPEAK_TAG "SetParams success");
    return true;
}

void EspeakPlayer::WriteToQueue(short *wav, int numsamples)
{
    HILOG_DEBUG(ESPEAK_TAG "WriteToQueue, numsamples:%{public}d", numsamples);
    if(!wav){
        //结束标志
        if(numsamples==-1){
            HILOG_DEBUG(ESPEAK_TAG "WriteToQueue end");
            wavQueue_.PushEnd();
            return;
        }else{
            HILOG_ERROR(ESPEAK_TAG "WriteToQueue failed");
            return;
        }
    }
    wavQueue_.Push(wav, numsamples);
}

void EspeakPlayer::Interrupt()
{
    HILOG_DEBUG(ESPEAK_TAG "Interrupt");
    wavQueue_.Stop();
    wavQueue_.Clear();
    interruptFlag_ = true;
}

bool EspeakPlayer::IsPlaying()
{
    return !wavQueue_.IsEmpty();
}

// 未加锁，只在PlayBackThread中调用
void EspeakPlayer::WriteToRenderer(uint8_t *wav, int length){
    size_t bytesToWrite = static_cast<size_t>(length);
    size_t bytesWritten = 0;
    HILOG_DEBUG(ESPEAK_TAG "start to write from buffer, bytesToWrite:%{public}zu", bytesToWrite);
    // 分多次写入样本数据，单词写入4096bytes，目的是让播放线程能够及时响应中断信号
    while(bytesWritten < bytesToWrite){
        if(bytesWritten + SINGLE_WRITE_BYTES <= bytesToWrite){
            bytesWritten += renderer_->Write(wav + bytesWritten, SINGLE_WRITE_BYTES);
        }else{
            bytesWritten += renderer_->Write(wav + bytesWritten, bytesToWrite - bytesWritten);
        }
        HILOG_DEBUG(ESPEAK_TAG "%{public}zu bytes have been written", bytesWritten);
        if (bytesWritten < 0 || interruptFlag_ || exitFlag_) break;
    }
    HILOG_DEBUG(ESPEAK_TAG "write over, bytesWritten: %{public}zu, bytesToWrite: %{public}zu", bytesWritten, bytesToWrite);
}

//清空renderer的时机有两个：1、单次播放完成；2、中断信号到来
void EspeakPlayer::PlayBackThread()
{
    HILOG_DEBUG(ESPEAK_TAG "PlayBackThread start to play");
    while(!exitFlag_){
        if(renderer_->GetStatus()!=2){
            HILOG_DEBUG(ESPEAK_TAG "renderer start");
            if(!renderer_->Start()){
                HILOG_ERROR(ESPEAK_TAG "renderer start failed");
            }
        }
        
        uint8_t *wav = nullptr;
        int length = wavQueue_.Pop(&wav);
        if(length > 0){
            //将有效wav数据写入renderer渲染
            WriteToRenderer(wav, length);
        }else if(length == -1){
            //结束标志，wav已合成并写入完毕
            HILOG_DEBUG(ESPEAK_TAG "synthesis and write over");
            // 清空renderer缓冲区已渲染的数据
            if(!renderer_->Drain()){
                HILOG_DEBUG(ESPEAK_TAG "renderer drain failed");
            }
        }
        free(wav);

        //中断信号到来，停止播放，清空render缓冲区；无需考虑wavqueue，播放线程只负责读取wavqueue以及操控renderer
        if(interruptFlag_ || exitFlag_){
            HILOG_DEBUG(ESPEAK_TAG "PlayBackThread interrupted");
            if(!renderer_->Flush()||!renderer_->Stop()){
                HILOG_ERROR(ESPEAK_TAG "PlayBackThread Flush or Stop failed");
            }
            if(interruptFlag_){
                interruptFlag_ = false;
            }
        }
    }
    HILOG_DEBUG(ESPEAK_TAG "PlayBackThread exit");
}

} // namespace Accessibility
} // namespace OHOS